package com.sparrow.common.metric;

import com.sparrow.common.metric.domain.Counter;
import com.sparrow.common.metric.domain.Gauge;
import com.sparrow.common.metric.domain.PerfKey;
import com.sparrow.common.metric.domain.PerfTimer;
import com.sparrow.common.util.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;

/**
 * 性能度量
 * Counter（计数器）单向累加
 * Gauge（仪表盘/即时值） 例如统计在线玩家数量，数据库保存队列长度，请求队列长度 等
 * Timer 时间段统计操作平均耗时、最大耗时、调用次数，打印后会重置所有timer
 */
public class MetricsManager {

    private static final Logger logger = LoggerFactory.getLogger(MetricsManager.class);

    private static final Map<PerfKey, PerfTimer> perfTimers = new ConcurrentHashMap<>();
    private static final Map<String, Counter> counters = new ConcurrentHashMap<>();
    private static final Map<String, Gauge> gauges = new ConcurrentHashMap<>();

    private static final long PRINT_INTERVAL_SEC = PropertiesUtil.getPropertyAsIntOr("metrics.print.interval", 10);

    private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
        Thread t = new Thread(r, "Metrics-thread");
        t.setDaemon(true);
        return t;
    });


    private static final ScheduledFuture<?> future;
    static {
        future = scheduler.scheduleAtFixedRate(() -> {
            try {
                MetricsManager.reportAll();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, PRINT_INTERVAL_SEC, PRINT_INTERVAL_SEC, TimeUnit.SECONDS);
    }

    /** Counter */
    public static Counter counter(String name) {
        return counters.computeIfAbsent(name, k -> new Counter(name));
    }

    /** Gauge */
    // ================= Gauge =================
    public static Gauge gauge(String name, Supplier<Double> func) {
        return gauges.computeIfAbsent(name, n -> new Gauge(name, func));
    }

    /** PerfTimer */
    public static PerfTimer timer(String name, String... tags) {
        if (tags.length % 2 != 0) {
            throw new IllegalArgumentException("Tags should be key-value pairs.");
        }
        Map<String, String> tagMap = new HashMap<>();
        for (int i = 0; i < tags.length; i += 2) {
            tagMap.put(tags[i], tags[i + 1]);
        }

        PerfKey key = new PerfKey(name, tagMap);
        return perfTimers.computeIfAbsent(key, k -> new PerfTimer(key));
    }

    /** 统一打印表格 */
    public static void reportAll() {
        StringBuilder sb = new StringBuilder(1024);
        sb.append("\n==================== Metrics Summary ====================\n");

        // Counter
        if (!counters.isEmpty()) {
            sb.append("| Counter Name           | Value      |\n");
            sb.append("|------------------------|------------|\n");
            counters.forEach((name, counter) ->
                    sb.append(String.format("| %-22s | %10d |\n", name, counter.get())));
            sb.append("|-------------------------------------|\n");
        }

        // Gauge
        if (!gauges.isEmpty()) {
            sb.append("| Gauge Name             | Value      |\n");
            sb.append("|------------------------|------------|\n");
            gauges.forEach((name, gauge) -> {
                double value = gauge.getValue();
                sb.append(String.format("| %-22s | %10.2f |\n", name, value));
            });
            sb.append("|-------------------------------------|\n");
        }

        // PerfTimer
        if (!perfTimers.isEmpty()) {
            sb.append("| PerfTimer Name                 | Count  | Avg(ms) | Max(ms) | Total(ms)\n");
            sb.append("|--------------------------------|--------|---------|---------|---------|\n");
            perfTimers.values().stream()
                    .sorted((a, b) -> Long.compare(b.getCount(), a.getCount()))
                    .filter(e -> e.hasData())
                    .forEach(timer -> sb.append(String.format("| %-30s | %6s | %7.2f | %7.2f | %7.2f |\n",
                            timer.getName(), timer.getCount(), timer.getAvg(), timer.getMax(), timer.getTotal())));
            sb.append("\n");
        }

        sb.append("===========================================================\n");
        logger.info(sb.toString());

        perfTimers.values().forEach(PerfTimer::reset);
    }

    public static void shutdown() {
        if (future != null) {
            future.cancel(true);
        }
        scheduler.shutdown(); // 停止接收新任务
        try {
            if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                scheduler.shutdownNow(); // 强制关闭
            }
        } catch (InterruptedException e) {
            scheduler.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}
